﻿#if UNITY_NETWORK
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.Networking.Match;
using UnityEngine.Networking.Types;


namespace Manox.Libs.Networking.Unity
{
    /// <summary>
    /// Unity network host.
    /// </summary>
    public class UnityNetworkHost : UnityNetworkNode, INetworkHost
    {
        /// <summary>Host created.</summary>
        public bool HostCreated { get; private set; }
        /// <summary>Create response.</summary>
        public NetworkResponseDelegate<bool> CreateResponse;
        /// <summary>Connections.</summary>
        public List<UnityNetworkConnectionId> Connections;

        /// <summary>Client connected.</summary>
        public event ConnectionChangedDelegate ClientConnected;
        /// <summary>Client connected.</summary>
        public event ConnectionChangedDelegate ClientDisconnected;


        /// <summary>
        /// STart host.
        /// </summary>
        /// <param name="hostName"></param>
        /// <param name="playersCount"></param>
        /// <param name="publicVisible"></param>
        /// <param name="password"></param>
        public void StartHost(string hostName, uint playersCount, bool publicVisible, string password, NetworkResponseDelegate<bool> response)
        {
            this.CreateResponse = response;

            this.UnityNetworkMatch.CreateMatch(hostName, playersCount, publicVisible, password, this.OnMatchCreate);

            this.Connections = new List<UnityNetworkConnectionId>();
        }

        /// <summary>
        /// On match create.
        /// </summary>
        /// <param name="matchResponse"></param>
        public void OnMatchCreate(CreateMatchResponse matchResponse)
        {
            if (matchResponse.success)
            {
                Debug.Log("Create match succeeded");

                Utility.SetAccessTokenForNetwork(matchResponse.networkId,
                    new NetworkAccessToken(matchResponse.accessTokenString));

                this.HostCreated = true;
                this.MatchInfo = new MatchInfo(matchResponse);

                this.StartServer(matchResponse.address, matchResponse.port, matchResponse.networkId, matchResponse.nodeId);
            }
            else
            {
                Debug.LogError("Create match failed");

                this.CreateResponse(false);

                this.CreateResponse = null;
            }
        }

        /// <summary>
        /// Start server.
        /// </summary>
        /// <param name="relayIp"></param>
        /// <param name="relayPort"></param>
        /// <param name="networkId"></param>
        /// <param name="nodeId"></param>
        private void StartServer(string relayIp, int relayPort, NetworkID networkId, NodeID nodeId)
        {
            this.SetupHost(true);

            byte error;

            NetworkTransport.ConnectAsNetworkHost(
                this.HostId, relayIp, relayPort, networkId, Utility.GetSourceID(), nodeId, out error);

            if (error == 0)
                this.CreateResponse(true);
            else
                this.CreateResponse(false);

            this.CreateResponse = null;
        }       

        /// <summary>
        /// On connect received.
        /// </summary>
        /// <param name="connectionId"></param>
        /// <param name="channelId"></param>
        /// <param name="buffer"></param>
        /// <param name="receivedSize"></param>
        public override void OnConnectReceived(UnityNetworkConnectionId connectionId, int channelId, byte[] buffer, int receivedSize)
        {
            base.OnConnectReceived(connectionId, channelId, buffer, receivedSize);

            if (!this.Connections.Contains(connectionId))
                this.Connections.Add(connectionId);            

            if (this.ClientConnected != null)
                this.ClientConnected(this, connectionId);
        }

        /// <summary>
        /// On disconnect received.
        /// </summary>
        /// <param name="connectionId"></param>
        /// <param name="channelId"></param>
        /// <param name="buffer"></param>
        /// <param name="receivedSize"></param>
        public override void OnDisconnectReceived(UnityNetworkConnectionId connectionId, int channelId, byte[] buffer, int receivedSize)
        {
            base.OnDisconnectReceived(connectionId, channelId, buffer, receivedSize);

            if (this.Connections.Contains(connectionId))
                this.Connections.Remove(connectionId);

            if (this.ClientDisconnected != null)
                this.ClientDisconnected(this, connectionId);
        }

        /// <summary>
        /// On data received.
        /// </summary>
        /// <param name="connectionId"></param>
        /// <param name="channelId"></param>
        /// <param name="buffer"></param>
        /// <param name="receivedSize"></param>
        public override void OnDataReceived(UnityNetworkConnectionId connectionId, int channelId, byte[] buffer, int receivedSize)
        {
            base.OnDataReceived(connectionId, channelId, buffer, receivedSize);

            byte error;

            for (int i = this.Connections.Count - 1; i >= 0; i--)
                if (this.Connections[i] != connectionId)
                    NetworkTransport.Send(
                        this.HostId, (this.Connections[i] as UnityNetworkConnectionId).ConnectionId, 0, buffer, receivedSize, out error);            
        }
    }    
}
#endif